/**
 * Copyright 2008 floop.pl
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 *  
 * http://www.apache.org/licenses/LICENSE-2.0 
 *  
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package org.eaicompiler.model;

import static java.lang.Math.max;

import java.util.ArrayList;
import java.util.Iterator;

import org.antlr.runtime.tree.Tree;
import org.eaicompiler.antlr.EventAIParser;
import org.eaicompiler.antlr.EventAIParser.integerList_return;

/**
 * Script object Holds all the information about script
 * 
 * @author floop.pl
 */
public class ScriptDefinition {
	// The only supported script type for now
	public final int					TYPE_CREATURE		= 1;

	private int							type				= TYPE_CREATURE;
	private ArrayList<Integer>			creatureFor			= new ArrayList<Integer>();
	private ArrayList<LocalizedText>	localizedText		= new ArrayList<LocalizedText>();
	private ArrayList<Event>			events				= new ArrayList<Event>();
	private ArrayList<Spawn>			spawns				= new ArrayList<Spawn>();
	private int							textIndex			= 1;								// 0
																								// indicates
																								// no
																								// text
	private int							spawnIndex			= 1;
	private int							maxPhase			= 0;
	private long						inversePhaseMask	= ~0;
	private boolean						phasesCalculated	= false;

	public ArrayList<Integer> getCreatureFor() {
		return creatureFor;
	}

	public void addEvent(Event e) {
		e.setInversePhaseMask(inversePhaseMask);
		events.add(e);
	}

	public ArrayList<Event> getEvents() {
		if (!phasesCalculated) {
			calculatePhases();
			phasesCalculated = true;
		}
		return events;
	}

	public void addCreatureFor(int id) {
		this.getCreatureFor().add(new Integer(id));
	}

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((creatureFor == null) ? 0 : creatureFor.hashCode());
		result = prime * result + ((localizedText == null) ? 0 : localizedText.hashCode());
		result = prime * result + type;
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final ScriptDefinition other = (ScriptDefinition) obj;
		if (creatureFor == null) {
			if (other.creatureFor != null)
				return false;
		} else if (!creatureFor.equals(other.creatureFor))
			return false;
		if (localizedText == null) {
			if (other.localizedText != null)
				return false;
		} else if (!localizedText.equals(other.localizedText))
			return false;
		if (type != other.type)
			return false;
		return true;
	}

	/**
	 * Adds the localized text to the script
	 * 
	 * @param localizedText
	 * @return index of the text used
	 */
	public int addLocalizedLanguage(LocalizedText text) {
		int index = textIndex;
		text.setId(textIndex++);
		localizedText.add(text);
		return index;
	}

	/**
	 * Adds spawn to the script
	 * 
	 * @param spawn
	 * @return index of the spawn used
	 */
	public int addSpawn(Spawn spawn) {
		int index = spawnIndex;
		spawn.setId(spawnIndex++);
		spawns.add(spawn);
		return index;
	}	
	
	/**
	 * @return the localizedText
	 */
	public ArrayList<LocalizedText> getLocalizedText() {
		return localizedText;
	}

	/**
	 * @return the spawns
	 */
	public ArrayList<Spawn> getSpawns() {
		return spawns;
	}

	public void setPhase(integerList_return phases) {
		Tree t = (Tree) phases.getTree();
		long phasesMask = 0;
		if (t.getChildCount() > 0) {
			for (int f = 0; f < t.getChildCount(); f++) {
				switch (t.getChild(f).getType()) {
					case EventAIParser.IntegerLiteral: {
						int phase = Integer.parseInt(t.getChild(f).getText()) - 1;
						maxPhase = max(phase, maxPhase);
						phasesMask = phasesMask | (1 << phase);
					}
						break;
				}
			}
		} else {
			int phase = Integer.parseInt(t.getText()) - 1;
			maxPhase = max(phase, maxPhase);
			phasesMask = phasesMask | (1 << phase);
		}
		inversePhaseMask = ~phasesMask;
	}

	private void calculatePhases() {
		long mask = 0;
		for (int f = 0; f <= maxPhase; f++) {
			mask = mask << 1;
			mask |= 1;
		}
		Iterator<Event> i = events.iterator();
		while (i.hasNext()) {
			Event e = i.next();
			e.setInversePhaseMask(e.getInversePhaseMask() & mask);
		}
	}

	public void setPhaseAll() {
		inversePhaseMask = 0;
	}

	public void setupCreatureFor(integerList_return creatureList) {
		Tree t = (Tree) creatureList.getTree();
		if (t.getChildCount() > 0) {
			for (int f = 0; f < t.getChildCount(); f++) {
				switch (t.getChild(f).getType()) {
					case EventAIParser.IntegerLiteral: {
						addCreatureFor(Integer.parseInt(t.getChild(f).getText()));
					}
						break;
				}
			}
		} else {
			addCreatureFor(Integer.parseInt(t.getText()));
		}
	}
}
